/*
 * @Author: TianyiWang
 * @Date: 2022-06-08 09:55:54
 * @LastEditors: TianyiWang
 * @LastEditTime: 2022-06-13 19:30:04
 * @Description: 日志模块头文件
 * @FilePath: /TinyWebServer/home/wty/sylar_learn/sylar/log.h
 */


#ifndef __SYLAR_LOG_H__
#define __SYLAR_LOG_H__

#include <string>
#include <list>
#include<stdint.h>
#include<sstream>
#include<fstream>
#include<memory>
#include<iostream>
#include<vector>
#include<map>
#include<functional>
#include"singleton.h"
#include"util.h"
#include <yaml-cpp/yaml.h>
#include"thread.h"

/*
* 使用流方式将日志级别为level的日志内容写入到logger
* 如果当前logger的日志级别小于参数level，那么重新创建一个LogEvent,使用LogEventWrap封装的
* 如果需要外界调用宏需要加入命名空间
*/
#define SYLAR_LOG_LEVEL(logger, level)\
    if (logger->getLevel() <= level) \
        sylar::LogEventWrap(sylar::LogEvent::ptr(new sylar::LogEvent(logger, level, \
                __FILE__, __LINE__, 0, sylar::GetThreadId(),\
                sylar::GetFiberId(), time(0), sylar::Thread::GetName()))).getSS()

/**
 * @brief 使用流式方式将日志级别debug的日志写入到logger
 */
#define SYLAR_LOG_DEBUG(logger) SYLAR_LOG_LEVEL(logger, sylar::LogLevel::DEBUG)

/**
 * @brief 使用流式方式将日志级别info的日志写入到logger
 */
#define SYLAR_LOG_INFO(logger) SYLAR_LOG_LEVEL(logger, sylar::LogLevel::INFO)

/**
 * @brief 使用流式方式将日志级别warn的日志写入到logger
 */
#define SYLAR_LOG_WARN(logger) SYLAR_LOG_LEVEL(logger, sylar::LogLevel::WARN)

/**
 * @brief 使用流式方式将日志级别error的日志写入到logger
 */
#define SYLAR_LOG_ERROR(logger) SYLAR_LOG_LEVEL(logger, sylar::LogLevel::ERROR)

/**
 * @brief 使用流式方式将日志级别fatal的日志写入到logger
 */
#define SYLAR_LOG_FATAL(logger) SYLAR_LOG_LEVEL(logger, sylar::LogLevel::FATAL)

/*
* 使用格式化的方式将日志级别level的日志写入到logger
*/
#define SYLAR_LOG_FMT_LEVEL(logger, level, fmt, ...) \
        if(logger->getLevel()<=level) \
        sylar::LogEventWrap(sylar::LogEvent::ptr(new sylar::LogEvent(logger, level, \
        __FILE__, __LINE__, 0, sylar::GetThreadId(), sylar::GetFiberId(),\
        time(0), sylar::Thread::GetName()))).getEvent()->format(fmt, __VA_ARGS__)
/**
 * @brief 使用格式化方式将日志级别Level的日志写入到logger
 */
#define SYLAR_LOG_FMT_DEBUG(logger, fmt, ...) SYLAR_LOG_FMT_LEVEL(logger, sylar::LogLevel::DEBUG, fmt, __VA_ARGS__)

#define SYLAR_LOG_FMT_INFO(logger, fmt, ...)  SYLAR_LOG_FMT_LEVEL(logger, sylar::LogLevel::INFO, fmt, __VA_ARGS__)

#define SYLAR_LOG_FMT_WARN(logger, fmt, ...)  SYLAR_LOG_FMT_LEVEL(logger, sylar::LogLevel::WARN, fmt, __VA_ARGS__)

#define SYLAR_LOG_FMT_ERROR(logger, fmt, ...) SYLAR_LOG_FMT_LEVEL(logger, sylar::LogLevel::ERROR, fmt, __VA_ARGS__)

#define SYLAR_LOG_FMT_FATAL(logger, fmt, ...) SYLAR_LOG_FMT_LEVEL(logger, sylar::LogLevel::FATAL, fmt, __VA_ARGS__)

/// 获取主日志器
#define SYLAR_LOG_ROOT() sylar::LoggerMgr::GetInstance()->getRoot()
/// 获取name日志器
#define SYLAR_LOG_NAME(name) sylar::LoggerMgr::GetInstance()->getLogger(name)

namespace sylar
{

class Logger;

class LogLevel{
public:
    enum Level{
        UNKNOWN = 0,
        DEBUG = 1,
        INFO = 2,
        WARN = 3,
        ERROR = 4,
        FATAL = 5
    };
    static const char* ToString(LogLevel::Level val);

    static LogLevel::Level FromString(const std::string& str);

};

class LogEvent
{
public:   
    using ptr = std::shared_ptr<LogEvent>;
    /**
     * @brief 构造函数
     * @param[in] logger 日志器
     * @param[in] level 日志级别
     * @param[in] file 文件名
     * @param[in] line 文件行号
     * @param[in] elapse 程序启动依赖的耗时(毫秒)
     * @param[in] thread_id 线程id
     * @param[in] fiber_id 协程id
     * @param[in] time 日志事件(秒)
     * @param[in] thread_name 线程名称
     */
    LogEvent(std::shared_ptr<Logger> logger, LogLevel::Level level
            ,const char* file, int32_t line, uint32_t elapse
            ,uint32_t thread_id, uint32_t fiber_id, uint64_t time
            ,const std::string& thread_name);

    LogEvent(std::shared_ptr<Logger> logger,const char* file, int32_t line, uint32_t elapse
            ,uint32_t thread_id, uint32_t fiber_id, uint64_t time);

    LogEvent(std::shared_ptr<Logger> logger, LogLevel::Level level
            ,const char* file, int32_t line, uint32_t elapse
            ,uint32_t thread_id, uint32_t fiber_id, uint64_t time);

    const char* getFile() const{ return m_file;}
    int32_t getLine() const{return m_line;}
    uint32_t getElapse() const{ return m_elapse;}
    uint32_t getThreadId() const{ return m_threadId;}
    uint64_t getFiberId() const{ return m_fiberId;}
    uint64_t getTime() const { return m_time;}
    const std::string getContent() const { return m_ss.str();}
    std::stringstream& getSS() { return m_ss;}  // 返回日志内容字符串流
    const std::string &getThreadName() const { return m_threadName; }
    LogLevel::Level getLevel() const{ return m_level;}
    std::string getName() const {return m_loggerName;}
    std::shared_ptr<Logger> getLogger() const{return m_logger;}

    /// 格式化写入日志内容
    void format(const char *fmt, ...);
    void format(const char *fmt, va_list al);
private:
    /// 日志级别
    LogLevel::Level m_level;
    /// 日志内容，使用stringstream存储，便于流式写入日志, 相当于是内容流，和FormatItem里的format流不是一回事
    std::stringstream m_ss;
    /// 文件名
    const char *m_file = nullptr;
    /// 行号
    int32_t m_line = 0;
    /// 从日志器创建开始到当前的耗时
    uint32_t m_elapse = 0;
    /// 线程id
    uint32_t m_threadId = 0;
    /// 协程id
    uint64_t m_fiberId = 0;
    /// UTC时间戳
    time_t m_time;
    /// 线程名称
    std::string m_threadName;
    /// 日志器名称
    std::string m_loggerName;
    /// 日志器，需要知道event事件写入到了哪个logger
    std::shared_ptr<Logger> m_logger;  
};

/*
* 封装event，在析构的时候将event写入log
*/
class LogEventWrap{
public:
    LogEventWrap(LogEvent::ptr event);
    /// 结合SYLAR_LOG_LEVLE使用，析构的时候输出log
    ~LogEventWrap();
    
    LogEvent::ptr getEvent() const { return m_event; }
    std::stringstream& getSS(){ return m_event->getSS();}
private:
    LogEvent::ptr m_event;
};

/*
* log格式化输出
* 将LogEvent 格式化成string，提供给Appender输出
*/
class LogFormatter{
public:
    using ptr = std::shared_ptr<LogFormatter>;
    
    /**
     * @brief 构造函数
     * @param[in] pattern 格式模板，参考sylar与log4cpp
     * @details 模板参数说明：
     * - %%m 消息
     * - %%p 日志级别
     * - %%c 日志器名称
     * - %%d 日期时间，后面可跟一对括号指定时间格式，比如%%d{%%Y-%%m-%%d %%H:%%M:%%S}，这里的格式字符与C语言strftime一致
     * - %%r 该日志器创建后的累计运行毫秒数
     * - %%f 文件名
     * - %%l 行号
     * - %%t 线程id
     * - %%F 协程id
     * - %%N 线程名称
     * - %%% 百分号
     * - %%T 制表符
     * - %%n 换行
     * 
     * 默认格式：%%d{%%Y-%%m-%%d %%H:%%M:%%S}%%T%%t%%T%%N%%T%%F%%T[%%p]%%T[%%c]%%T%%f:%%l%%T%%m%%n
     * 
     * 默认格式描述：年-月-日 时:分:秒 [累计运行毫秒数] \\t 线程id \\t 线程名称 \\t 协程id \\t [日志级别] \\t [日志器名称] \\t 文件名:行号 \\t 日志消息 换行符
     */
    LogFormatter(const std::string &pattern);
    std::string format(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event);
    std::string getPattern() const { return m_pattern;}
public:
    class FormatItem{
    public:
        using ptr = std::shared_ptr<FormatItem>;
        FormatItem(const std::string fmt=""){}
        virtual ~FormatItem(){}
        /// 输出ostream流的格式
        virtual void format(std::ostream& os, std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event)=0;
    };

    void init(); 
    bool isError() const{ return m_error;}
private:
    std::string m_pattern;
    std::vector<FormatItem::ptr> m_items;
    bool m_error = false;
};


/*
* 日志输出器，输出地点,针对不同日志输出不同的级别
* 依赖LogLevel LogFormatter
*/
class LogAppender
{
    friend class Logger;
public:
    using ptr = std::shared_ptr<LogAppender>;
    virtual ~LogAppender() {}
    /// 日志输出到不同地点
    virtual void log(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) = 0;
    
    /// 设置日志格式
    void setFormatter(LogFormatter::ptr val){ 
        // 设置格式的时候，可能会有人在读
        Spinlock::Lock lock(m_mutex);
        m_formatter = val;
        if(m_formatter){
            m_hasFormatter = true;
        }else{
            m_hasFormatter = false;
        }
    }
    LogFormatter::ptr getFormatter(){
        Spinlock::Lock lock(m_mutex);
        return m_formatter;
    }

    /// 设置日志等级  可能会需要根据不同的日志级别保存日志
    void setLevel(LogLevel::Level level){ m_level = level;}
    LogLevel::Level getLevel(){return m_level;}
    virtual std::string toYamlString()=0;
protected:
    /// 添加好初始化可以正常输出log
    LogLevel::Level m_level = LogLevel::INFO;
    ///LogLevel::Level m_level;
    LogFormatter::ptr m_formatter;  
    /// 是否有自己的格式器
    bool m_hasFormatter = false; 

    Spinlock m_mutex;  //写的时候比较多
};

/// 输出到控制台的Appender
class StdoutLogAppender:public LogAppender{

public:
    using ptr = std::shared_ptr<StdoutLogAppender>;
    void log(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) override;
    std::string toYamlString();
private:

};

/// 输出到文件的Appender
class FileLogAppender: public LogAppender{
public:
    using ptr = std::shared_ptr<FileLogAppender>;
    FileLogAppender(const std::string &filename):m_filename(filename){ reopen();}
    void log(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) override;
    bool reopen();   ///若果文件打开了，那么关闭然后重新打开
    std::string toYamlString();
private:
    std::string m_filename;
    std::ofstream m_filestream;

    uint64_t m_lastTime = 0;  //检查文件是否被误删
};

class Logger: public std::enable_shared_from_this<Logger>
{
public:
    friend class LoggerManager;
    using ptr = std::shared_ptr<Logger>;
    
    Logger(const std::string &name="root");
    ///输出日志
    void log(LogLevel::Level level, LogEvent::ptr event);

    ///输出不同级别的日志
    void debug(LogEvent::ptr event);
    void info(LogEvent::ptr event);
    void warn(LogEvent::ptr event);
    void error(LogEvent::ptr event);
    void fatal(LogEvent::ptr event);

    void addAppender(LogAppender::ptr appender);
    void delAppender(LogAppender::ptr appender);
    void clearAppenders();

    ///设置level级别
    void setLevel(LogLevel::Level val){m_level = val;}
    LogLevel::Level getLevel() const{return m_level;}

    void setFormatter(const std::string str);
    void setFormatter(LogFormatter::ptr ptr);
    LogFormatter::ptr getFormatter();

    const std::string& getName() const { return m_name;}

    YAML::Node toYamlString();

private:
    std::string m_name;               ///日志器名称
    LogLevel::Level m_level;          ///日志级别
    std::list<LogAppender::ptr>  m_appenders;  ///Appender集合
    ///日志格式器
    LogFormatter::ptr m_formatter; 
    ///主日志器
    Logger::ptr m_root;                
    Spinlock m_mutex;
};

class LoggerManager{
public:
    LoggerManager();
    /// 初始化
    void init();
    Logger::ptr getLogger(const std::string& name);

    /// 返回主日志器
    Logger::ptr getRoot(){return m_root;}

    /// 将日志器配置转化为 YAML string
    std::string toYamlString();
    

private:
    std::map<std::string, Logger::ptr> m_loggers;
    /// 主日志器 构造时进行初始化
    Logger::ptr m_root;
    Spinlock m_mutex;

};

using LoggerMgr = sylar::Singleton<LoggerManager>;

struct LogDefine;
struct LogAppenderDefine;

}

#endif